/** @file

  Copyright (c) 2021, Baruch Binyamin Doron
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#ifndef LGMR_ENABLED
// TODO: Consider actually enforcing mutex?
Mutex (BMTX, 0)
#endif
Name (B0ST, 0)  /* Battery 0 status */

/*
 * EC Registers
 *
 * "EBID" is the battery page selector.
 *
 *
 * Data on the 128 bits following offset
 * 0xE0 is accessed in the following order:
 *
 * Information:
 *  Page 0: EBCM  # start on page 0 #
 *  Page 0: EBFC
 *  Page 1: EBDC  # switch to page 1 #
 *  Page 1: EBDV
 *  Page 1: EBSN
 *  Page 3: EBDN  # switch to page 3 #
 *  Page 4: EBCH  # switch to page 4 #
 *  Page 2: EBMN  # switch to page 2 #
 *
 * Status:
 *  Page 0: EBAC  # start on page 0 #
 *  Page 0: EBRC
 *  Page 0: EBFC
 *  Page 0: EBVO
 */
/* Page 0 */
Field (RAM, ByteAcc, Lock, Preserve)
{
  Offset (0xE0),
  EBRC, 16,  /* Battery remaining capacity */
  EBFC, 16,  /* Battery full charge capacity */
  EBPE, 16,
  EBAC, 16,  /* Battery present rate */
  EBVO, 16,  /* Battery voltage */
      , 15,
  EBCM, 1,   /* Battery charging */
  EBCU, 16,
  EBTV, 16,
}

/* Page 1 */
Field (RAM, ByteAcc, Lock, Preserve)
{
  Offset (0xE0),
  EBDC, 16,  /* Battery design capacity */
  EBDV, 16,  /* Battery design voltage */
  EBSN, 16,  /* Battery serial number */
}

/* Page 2 */
Field (RAM, ByteAcc, NoLock, Preserve)
{
  Offset (0xE0),
  EBMN, 128,  /* Battery manufacturer */
}

/* Page 3 */
Field (RAM, ByteAcc, NoLock, Preserve)
{
  Offset (0xE0),
  EBDN, 128,  /* Battery model */
}

/* Page 4 */
Field (RAM, ByteAcc, NoLock, Preserve)
{
  Offset (0xE0),
  EBCH, 128,  /* Battery type */
}

#ifdef LGMR_ENABLED
OperationRegion (MBB0, SystemMemory, (LGMR + 0x80), 0xFF)
Field (MBB0, ByteAcc, Lock, Preserve)
{
  MBRC, 16,
  MBFC, 16,
  MBPE, 16,
  MBAC, 16,
  MBVO, 16,
      , 15,
  MBCM, 1,
  MBCU, 16,
  MBTV, 16,
}

Field (MBB0, ByteAcc, Lock, Preserve)
{
  Offset (0x10),
  MBDC, 16,
  MBDV, 16,
  MBSN, 16,
}

Field (MBB0, ByteAcc, Lock, Preserve)
{
  Offset (0x40),
  MBMN, 128,
}

Field (MBB0, ByteAcc, Lock, Preserve)
{
  Offset (0x50),
  MBDN, 256,
}

Field (MBB0, ByteAcc, Lock, Preserve)
{
  Offset (0x70),
  MBCH, 128,
}
#endif

/*
 * Arg0: Battery number
 * Arg1: Battery Information Package
 * Arg2: Status
 */
#ifndef LGMR_ENABLED
Method (GBIF, 3, Serialized)
{
  Acquire (BMTX, 0xFFFF)  // Due to EC paging, don't run this with another function
#else
Method (GBIF, 3, NotSerialized)
{
#endif
  If (Arg2)
  {
    Arg1[1] = 0xFFFFFFFF
    Arg1[2] = 0xFFFFFFFF
    Arg1[4] = 0xFFFFFFFF
    Arg1[5] = 0
    Arg1[6] = 0
  }
  Else
  {
#ifdef LGMR_ENABLED
    Local0 = MBCM
#else
    EBID = 0  // We don't know which page was active
    Local0 = EBCM
#endif
    Arg1[0] = (Local0 ^ 1)

#ifdef LGMR_ENABLED
    Local2 = MBFC
    Local1 = MBDC
#else
    Local2 = EBFC
    EBID = 1
    Local1 = EBDC
#endif
    If (Local0)
    {
      Local2 *= 10
      Local1 *= 10
    }

    Arg1[1] = Local1  // Design capacity
    Arg1[2] = Local2  // Last full charge capacity
#ifdef LGMR_ENABLED
    Arg1[4] = MBDV    // Design voltage
#else
    Arg1[4] = EBDV    // Design voltage
#endif
    Local6 = (Local2 / 100)  // Warning capacities; Remainders ignored
    Arg1[5] = (Local6 * 7)  /* Low: 7% */
    Arg1[6] = ((Local6 * 11) / 2)  /* Very low: 5.5% */
#ifdef LGMR_ENABLED
    Local7 = MBSN
#else
    Local7 = EBSN
#endif
    Name (SERN, Buffer (0x06) { "     " })
    Local6 = 4
    While (Local7)
    {
      Divide (Local7, 10, Local5, Local7)
      SERN[Local6] = (Local5 + 0x30)  // Add ASCII 0x30 to get character
      Local6--
    }

    Arg1[10] = SERN  // Serial number
#ifdef LGMR_ENABLED
    Arg1[9] = MBDN  // Model number
    Arg1[11] = MBCH  // Battery type
    Arg1[12] = MBMN  // OEM information
#else
    EBID = 3
    Arg1[9] = EBDN  // Model number
    EBID = 4
    Arg1[11] = EBCH  // Battery type
    EBID = 2
    Arg1[12] = EBMN  // OEM information
#endif
  }

#ifndef LGMR_ENABLED
  Release (BMTX)
#endif
  Return (Arg1)
}

/*
 * Arg0: Battery number
 * Arg1: State information
 * Arg2: Power units
 * Arg3: Battery Status Package
 */
Method (GBST, 4, NotSerialized)  // All on one page
{
#ifndef LGMR_ENABLED
  Acquire (BMTX, 0xFFFF)  // Due to EC paging, don't run this with another function
#endif
  If (Arg1 & 0x02)  // BIT1 in "MB0S/EB0S"
  {
    Local0 = 2
    If (Arg1 & 0x20)  // "EB0F"
    {
      Local0 = 0
    }
  }
  ElseIf (Arg1 & 0x04)  // BIT2 in "MB0S/EB0S"
  {
    Local0 = 1
  }
  Else
  {
    Local0 = 0
  }

  If (Arg1 & 0x10)  // "EB0L"
  {
    Local0 |= 0x04
  }

  If (Arg1 & 0x01)  // "EB0A"
  {
    /*
     * Present rate is a 16bit signed int, positive while charging
     * and negative while discharging.
     */
#ifdef LGMR_ENABLED
    Local1 = MBAC
    Local2 = MBRC
    If (MACS)  // Charging
#else
    EBID = 0  // We don't know which page was active
    Local1 = EBAC
    Local2 = EBRC
    If (EACS)  // Charging
#endif
    {
      If (Arg1 & 0x20)  // "EB0F"
      {
#ifdef LGMR_ENABLED
        Local2 = MBFC
#else
        Local2 = EBFC
#endif
      }
    }

    If (Arg2)
    {
      Local2 *= 10
    }

#ifdef LGMR_ENABLED
    Local3 = MBVO
#else
    Local3 = EBVO
#endif
    /*
     * The present rate value should be positive unless discharging. If so,
     * negate present rate.
     */
    If (Local1 >= 0x8000)
    {
      If (Local0 & 0x01)
      {
        Local1 = (0x00010000 - Local1)
      }
      Else
      {
        Local1 = 0  // Full battery, force to 0
      }
    }
    /*
     * If that was not the case, we have an EC bug or inconsistency
     * and force the value to 0.
     */
    ElseIf ((Local0 & 0x02) == 0)
    {
      Local1 = 0
    }

    If (Arg2)
    {
      Local1 *= Local3
      Local1 /= 1000  /* Remainder ignored */
    }
  }
  Else
  {
    Local0 = 0
    Local1 = 0xFFFFFFFF
    Local2 = 0xFFFFFFFF
    Local3 = 0xFFFFFFFF
  }

  Arg3[0] = Local0
  Arg3[1] = Local1
  Arg3[2] = Local2
  Arg3[3] = Local3

#ifndef LGMR_ENABLED
  Release (BMTX)
#endif
  Return (Arg3)
}

Device (BAT0)
{
  Name (_HID, EisaId ("PNP0C0A") /* Control Method Battery */)  // _HID: Hardware ID
  Name (_UID, 0)  // _UID: Unique ID
  Name (_PCL, Package () { \_SB })  // _PCL: Power Consumer List

  Name (B0IP, Package (0x0D)
  {
    1,           /* 0x00: Power Unit: mAh */
    0xFFFFFFFF,  /* 0x01: Design Capacity */
    0xFFFFFFFF,  /* 0x02: Last Full Charge Capacity */
    1,           /* 0x03: Battery Technology: Rechargeable */
    0xFFFFFFFF,  /* 0x04: Design Voltage */
    0,           /* 0x05: Design Capacity of Warning */
    0,           /* 0x06: Design Capacity of Low */
    1,           /* 0x07: Capacity Granularity 1 */
    1,           /* 0x08: Capacity Granularity 2 */
    "",          /* 0x09: Model Number */
    "100",       /* 0x0a: Serial Number */
    "Lion",      /* 0x0b: Battery Type */
    0            /* 0x0c: OEM Information */
  })
  Name (B0SP, Package (0x04)
  {
    0,           /* 0x00: Battery State */
    0xFFFFFFFF,  /* 0x01: Battery Present Rate */
    0xFFFFFFFF,  /* 0x02: Battery Remaining Capacity */
    0xFFFFFFFF   /* 0x03: Battery Present Voltage */
  })
  Method (_STA, 0, NotSerialized)  // _STA: Status
  {
    Local1 = EB0A
    If (Local1 & 0x40)
    {
      Local1 = 0
    }

    B0ST = Local1
    If (Local1)
    {
      Return (0x1F)
    }
    Else
    {
      Return (0x0F)
    }
  }

  Method (_BIF, 0, NotSerialized)  // _BIF: Battery Information
  {
    Local6 = B0ST
    Local7 = 20
    While (Local6 && Local7)
    {
      If (EB0R)
      {
        Local6 = 0
      }
      Else
      {
        Sleep (500)
        Local7--
      }
    }

    Return (GBIF (0, B0IP, Local6))
  }

  Method (_BST, 0, NotSerialized)  // _BST: Battery Status
  {
    Local0 = (DerefOf (B0IP[0]) ^ 1)
#ifdef LGMR_ENABLED
    Local5 = MB0S
#else
    Local5 = EB0S
#endif
    Return (GBST (0, Local5, Local0, B0SP))
  }
}
